package frame.laxcen.module;

import frame.laxcen.BaseFrame;
import frame.laxcen.collection.ConstPair;
import frame.laxcen.dataobject.DataObject_0010_10_RFID;
import frame.laxcen.request.Request_0010_10;
import frame.laxcen.request.Request_0010_FF;
import frame.laxcen.response.Response_0001_12;
import frame.laxcen.response.Response_0010_10;
import frame.laxcen.util.FrameUtil;
import tcpclient.v2.RFIDClient;
import frame.util.FlowUtil;
import frame.util.Ref;

import java.net.InetSocketAddress;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class RFIDReader {

    private final RFIDClient client;

    public RFIDReader(RFIDClient client) {

        this.client = client;
    }

    // 尝试在3秒钟内连接读写器
    // param: address 服务器IP地址及端口号
    // return: 连接成功返回true 连接失败返回false
    public boolean tryConnect(InetSocketAddress address) {

        return client.tryConnect(address);
    }

    // 尝试在若干时间内连接读写器
    // param: address 服务器IP地址及端口号
    // param: millisecond 等待的毫秒数
    // return: 连接成功返回true 连接失败返回false
    public boolean tryConnect(InetSocketAddress address, long millisecond) {

        return client.tryConnect(address, true, millisecond);
    }

    // 尝试关闭与读写器之间的连接 并正常中断读回调
    // return: 关闭成功返回true 关闭失败返回false
    public boolean tryClose() {

        return client.tryClose();
    }

    // 向读写器发送读指令 并从读写器中获取返回数据 阻塞直到超时
    // param: request 推荐使用单次读指令  注 即使是单次读也可能返回多条数据
    // param: isUpload 返回读写器主动上传的信息列表 一般情况下为RFID标签数据
    // param: isNotUpload 返回读写器非主动上传的信息列表 一般情况下为读指令的反馈消息
    // param: millisecond 最长等待时间
    public void blockRead(Request_0010_10 request, Ref<List<BaseFrame>> isUpload, Ref<List<BaseFrame>> isNotUpload, long millisecond) {

        long begin = System.currentTimeMillis();

        if(!client.trySend(request, millisecond)) return;

        millisecond = millisecond - System.currentTimeMillis() + begin;

        List<BaseFrame> responseList = client.waitForResponseList(millisecond, (frame) -> frame.isResponse(Response_0010_10.class), Integer.MAX_VALUE);

        List<BaseFrame> isUploadList = new ArrayList<>(), isNotUploadList = new ArrayList<>();

        FrameUtil.split(responseList, isUploadList, isNotUploadList, (frame) -> frame.CW.IsUpload);

        if(isUploadList.size() > 0) isUpload.swap(isUploadList);

        if(isNotUploadList.size() > 0) isNotUpload.swap(isNotUploadList);
    }

    // 向读写器发送读指令 并从读写器中获取返回数据 阻塞直到超时
    // param: request 推荐使用单次读指令 注 即使是单次读也可能返回多条数据
    // param: millisecond 最长等待时间
    // return: 返回一个Set Set中的元素为一对字符串 第一个字段为标签的Tid 第二个字段为标签的UserData
    public Set<ConstPair<String, String>> blockRead(Request_0010_10 request, long millisecond) {

        long begin = System.currentTimeMillis();

        if(!client.trySend(request, millisecond)) return new HashSet<>();

        millisecond = millisecond - System.currentTimeMillis() + begin;

        List<BaseFrame> responseList = client.waitForResponseList(millisecond, (frame) -> frame.isResponse(Response_0010_10.class), Integer.MAX_VALUE);

        List<BaseFrame> isUploadList = new ArrayList<>(), isNotUploadList = new ArrayList<>();

        FrameUtil.split(responseList, isUploadList, isNotUploadList, (frame) -> frame.CW.IsUpload);

        isUploadList.removeIf(Response_0010_10.isStopResult);

        return isUploadList.stream()
                .map((frame) -> FlowUtil.silent(() -> new Response_0010_10(frame), Response_0010_10.class.cast(null)))
                .filter(((Predicate<? super Response_0010_10>) (Objects::isNull)).negate())
                .map(Response_0010_10::getRFIDDataObject)
                .map((object) -> ConstPair.of(object.getTid(), object.getUserData().trim()))
                .collect(Collectors.toSet());
    }

    // 向读写器发送读指令 并在获取到第一条数据后立马返回 否则阻塞直到超时
    // param: request 推荐使用单次读指令
    // param: millisecond 最长等待时间
    // return: 返回一个 ConstPair 第一个字段为标签的Tid 第二个字段为标签的UserData
    public Optional<ConstPair<String, String>> pointRead(Request_0010_10 request, long millisecond) {

        long begin = System.currentTimeMillis();

        if(!client.trySend(request, millisecond)) return Optional.empty();

        List<BaseFrame> responseList = client.waitForResponseList(millisecond - System.currentTimeMillis() + begin, Response_0010_10.isRFIDMessage, 1);

        if(responseList.isEmpty()) return Optional.empty();

        return Optional.of(responseList.get(0))
                .map((frame) -> FlowUtil.silent(() -> new Response_0010_10(frame), Response_0010_10.class.cast(null)))
                .map(Response_0010_10::getRFIDDataObject)
                .map((object) -> ConstPair.of(object.getTid(), object.getUserData().trim()));
    }

    // 向读写器发送读指令
    // param: request 推荐使用连续读读指令
    // param: millisecond 最长等待时间
    // return: 发送成功返回true 发送失败返回false
    public boolean beginRead(Request_0010_10 request, long millisecond) {

        return client.trySend(request, millisecond);
    }

    // 阻塞若干时间 并收集从读写器中返回的数据
    // param: millisecond 最长等待时间
    // return: 返回一个列表 保存从读写器中获得的RFID标签信息的POJO类 可以从类的实例中获取设置中要读取的RFID标签的属性
    public List<DataObject_0010_10_RFID> collectResponseList(long millisecond) {

        List<BaseFrame> responseList = client.waitForResponseList(millisecond, (frame) -> frame.isResponse(Response_0010_10.class), Integer.MAX_VALUE);

        List<BaseFrame> isUploadList = new ArrayList<>(), isNotUploadList = new ArrayList<>();

        FrameUtil.split(responseList, isUploadList, isNotUploadList, (frame) -> frame.CW.IsUpload);

        isUploadList.removeIf(Response_0010_10.isStopResult);

        return isUploadList.stream()
                .map((frame) -> FlowUtil.silent(() -> new Response_0010_10(frame), Response_0010_10.class.cast(null)))
                .filter(((Predicate<? super Response_0010_10>) (Objects::isNull)).negate())
                .map(Response_0010_10::getRFIDDataObject)
                .collect(Collectors.toList());
    }

    // 阻塞若干时间 并收集从读写器中返回的数据
    // param: millisecond 最长等待时间
    // return: 返回一个Set Set中的元素为一对字符串 第一个字段为标签的Tid 第二个字段为标签的UserData
    public Set<ConstPair<String, String>> collectResponseSet(long millisecond) {

        return collectResponseList(millisecond).stream()
                .map((response) -> ConstPair.of(response.getTid(), response.getUserData().trim()))
                .collect(Collectors.toSet());
    }

    // 向读写器发送停止读指令
    // param: millisecond 最长等待时间
    // return: 发送成功返回true 发送失败返回false
    public boolean endRead(long millisecond) {

        return client.trySend(new Request_0010_FF(), millisecond);
    }
}
